#ifndef __ASYNC_LOG_HPP__
#define __ASYNC_LOG_HPP__

#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <any>
#include <string>
#include <utility>
#include <sstream>
#include <thread>

#include "./util.hpp"

namespace yx
{
    // 日志等级
    enum LogLevel
    {
        DEBUG = 0,
        INFO = 1,
        WARN = 2,
        ERROR = 3,
        FATAL = 4,
        LOGLEVEL_COUNT = 5
    };

    static const std::string LogLevelDesc[LOGLEVEL_COUNT] = {
        "DEBUG: 0",
        "INFO: 1",
        "WARN: 2",
        "ERROR: 3",
        "FATAL: 4"};

    class LogTask
    {
    public:
        long _line;                  // 行号
        LogLevel _level;             // 等级
        std::string _file;           // 文件
        std::string _datetime;       // 时间
        std::queue<std::any> _datas; // 消息

        LogTask() = default;
        LogTask(const LogTask &src)
            : _line(src._line), _level(src._level), _file(src._file), _datetime(src._datetime), _datas(src._datas)
        {
        }
        LogTask(const LogTask &&src)
            : _line(src._line), _level(src._level), _file(std::move(src._file)), _datetime(std::move(src._datetime)), _datas(std::move(src._datas))
        {
        }
    };

    class AsyncLog
    {
    private:
        bool _isstop;                                      // 是否停止
        std::mutex _mtx;                                   // 互斥锁
        std::condition_variable _condvar;                  // 条件变量
        std::thread _workthread;                           // 写线程
        std::queue<std::shared_ptr<LogTask> > _blockqueue; // 任务队列

        AsyncLog()
            : _isstop(false)
        {
            _workthread = std::thread([this]()
                                      {
                for (;;)
                {
                    // 加锁
                    std::unique_lock<std::mutex> ul(_mtx);
                    // 队列空并且没有停止则进行等待
                    while (_blockqueue.empty() && !_isstop)
                    {
                        _condvar.wait(ul);
                    }
                    // 停止结束
                    if (_isstop)
                    {
                        return;
                    }
                    // 获取任务
                    auto logtask = _blockqueue.front();
                    _blockqueue.pop();
                    // 解锁
                    ul.unlock();
                    // 执行任务
                    processTask(std::move(logtask));
                } });
        }

        void processTask(std::shared_ptr<LogTask> logtask)
        {
            std::string str;
            // 等级
            str += "[";
            str += LogLevelDesc[logtask->_level];
            str += "]";
            // 时间
            str += logtask->_datetime;
            // 文件
            str += "[";
            str += logtask->_file;
            str += "]";
            // 行号
            str += "[";
            str += std::to_string(logtask->_line);
            str += "]";
            // 内容
            str += getMessage(logtask->_datas);

            // std::cout << str;
            std::cout << str << std::endl;
        }

        bool convertToStr(const std::any &data, std::string &str)
        {
            std::ostringstream ss;
            if (data.type() == typeid(int))
            {
                ss << std::any_cast<int>(data);
            }
            else if (data.type() == typeid(float))
            {
                ss << std::any_cast<float>(data);
            }
            else if (data.type() == typeid(double))
            {
                ss << std::any_cast<double>(data);
            }
            else if (data.type() == typeid(const std::string))
            {
                ss << std::any_cast<const std::string>(data);
            }
            else if (data.type() == typeid(std::string))
            {
                ss << std::any_cast<std::string>(data);
            }
            else if (data.type() == typeid(char *))
            {
                ss << std::any_cast<char *>(data);
            }
            else if (data.type() == typeid(char const *))
            {
                ss << std::any_cast<char const *>(data);
            }

            else
            {
                return false;
            }
            str = ss.str();
            return true;
        }

        std::string getMessage(std::queue<std::any> &datas)
        {
            // "编译{}文件成功", file_name
            // 获取队头元素
            auto head = datas.front();
            datas.pop();
            // 将队头元素转换成string
            std::string message;
            // 转换失败或者队列没有元素则返回
            if (!convertToStr(head, message) || datas.empty())
            {
                return message;
            }

            size_t pos = 0;
            while (!datas.empty())
            {
                // 获取元素
                auto data = datas.front();
                datas.pop();
                // 转成string
                std::string str;
                if (!convertToStr(data, str))
                {
                    continue;
                }
                size_t ret = StringUtil::oneReplace(message, "{}", str, pos);
                if (ret != std::string::npos)
                {
                    pos = ret - str.size();
                }
                else
                {
                    // 不能替换则直接追加到消息后面
                    message += str;
                }
            }

            return message;
        }

    public:
        // 获取实例
        static AsyncLog *getInstance()
        {
            static AsyncLog instance;
            return &instance;
        }
        // 设置stop
        void setStop()
        {
            _isstop = true;
            _condvar.notify_one();
        }
        // 析构
        ~AsyncLog()
        {
            setStop();
            _workthread.join();
        }

        AsyncLog &operator=(const AsyncLog &) = delete;
        AsyncLog(const AsyncLog &) = delete;

        template <typename... Args>
        void asyncWrite(LogLevel level, const std::string &file, long line, Args... args)
        {
            // 构建任务
            std::shared_ptr<LogTask> logtask = std::make_shared<LogTask>();
            logtask->_level = level;
            logtask->_file = file;
            logtask->_line = line;
            logtask->_datetime = TimeUtil::getDateTime();
            (logtask->_datas.push(args), ...);
            // 加锁
            std::unique_lock<std::mutex> ul(_mtx);
            // 推送任务
            _blockqueue.push(logtask);
            bool notify = (static_cast<int>(_blockqueue.size()) == 1) ? true : false;
            // 解锁
            ul.unlock();
            // 通知等待的线程有新的任务可处理
            if (notify)
            {
                _condvar.notify_one();
            }
        }
    };

    class FuncDLog
    {
    private:
        std::string _file;
        long _line;

    public:
        FuncDLog(const std::string &file, long line)
            : _file(file), _line(line)
        {
        }

        template <typename... Args>
        void operator()(Args &&...args)
        {
            AsyncLog::getInstance()->asyncWrite(DEBUG, _file, _line, std::forward<Args>(args)...);
        }
    };
    #define DLOG() FuncDLog(__FILE__, __LINE__)

    class FuncILOG
    {
    private:
        std::string _file;
        long _line;

    public:
        FuncILOG(const std::string &file, long line)
            : _file(file), _line(line)
        {
        }

        template <typename... Args>
        void operator()(Args &&...args)
        {
            AsyncLog::getInstance()->asyncWrite(INFO, _file, _line, std::forward<Args>(args)...);
        }
    };
    #define ILOG() FuncILOG(__FILE__, __LINE__)

    class FuncWLOG
    {
    private:
        std::string _file;
        long _line;

    public:
        FuncWLOG(const std::string &file, long line)
            : _file(file), _line(line)
        {
        }

        template <typename... Args>
        void operator()(Args &&...args)
        {
            AsyncLog::getInstance()->asyncWrite(WARN, _file, _line, std::forward<Args>(args)...);
        }
    };
    #define WLOG() FuncWLOG(__FILE__, __LINE__)

    class FuncELOG
    {
    private:
        std::string _file;
        long _line;

    public:
        FuncELOG(const std::string &file, long line)
            : _file(file), _line(line)
        {
        }

        template <typename... Args>
        void operator()(Args &&...args)
        {
            AsyncLog::getInstance()->asyncWrite(ERROR, _file, _line, std::forward<Args>(args)...);
        }
    };
    #define ELOG() FuncELOG(__FILE__, __LINE__)

    class FuncFLOG
    {
    private:
        std::string _file;
        long _line;

    public:
        FuncFLOG(const std::string &file, long line)
            : _file(file), _line(line)
        {
        }

        template <typename... Args>
        void operator()(Args &&...args)
        {
            AsyncLog::getInstance()->asyncWrite(FATAL, _file, _line, std::forward<Args>(args)...);
        }
    };
    #define FLOG() FuncFLOG(__FILE__, __LINE__)

    // template <typename... Args>
    // inline void DLog(Args &&...args)
    // {
    //     AsyncLog::getInstance()->asyncWrite(DEBUG, __FILE__, __LINE__, std::forward<Args>(args)...);
    // }

    // template <typename... Args>
    // inline void ILog(Args &&...args)
    // {
    //     AsyncLog::getInstance()->asyncWrite(INFO, __FILE__, __LINE__, std::forward<Args>(args)...);
    // }

    // template <typename... Args>
    // inline void WLog(Args &&...args)
    // {
    //     AsyncLog::getInstance()->asyncWrite(WARN, __FILE__, __LINE__, std::forward<Args>(args)...);
    // }

    // template <typename... Args>
    // inline void ELog(Args &&...args)
    // {
    //     AsyncLog::getInstance()->asyncWrite(ERROR, __FILE__, __LINE__, std::forward<Args>(args)...);
    // }

    // template <typename... Args>
    // inline void FLog(Args &&...args)
    // {
    //     AsyncLog::getInstance()->asyncWrite(FATAL, __FILE__, __LINE__, std::forward<Args>(args)...);
    // }
}
#endif // __ASYNC_LOG_HPP__